/*
 * plani.c
 *
 *  Created on: 22/09/2011
 *      Author: utn_so
 */

#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <semaphore.h>
#include <unistd.h>

#include "log.h"
#include "config.h"
#include "nipc.h"

#include "plani.h"
#include "raid.h"
#include "consola.h"

extern struct descriptor_t *tablaDesc, *tablaprox;
extern pthread_mutex_t descriptor;
extern int cantSecTotal;
unsigned int retardo = 5;

pthread_mutex_t l_ped = PTHREAD_MUTEX_INITIALIZER;
struct t_pedido* ultimo;

int enviarAOtroPlani(int idDisco)
{
	struct descriptor_t*	tabla = NULL;
	struct t_pedido*		aux= NULL;
	int sector, dPedido;
	unsigned char  pedido;

	if(tabla == NULL)
	{
		tabla = tablaDesc;
	}
	while((tabla!= NULL) && (tabla->idDisco != idDisco))
	{
		tabla = tablaDesc;
	}
	if(tabla == NULL)
	{
		return 0;
	}else{
		while(tabla->pedidos != NULL)
		{
			if(tabla->pedidos->type == read_c)
			{
				dPedido = tabla->pedidos->descriptorSoc;
				pedido = tabla->pedidos->type;
				sector = tabla->pedidos->sector;

				agregarPedido(dPedido, pedido, sector, NULL);
				aux = tabla->pedidos;
				tabla->pedidos = tabla->pedidos->siguiente;
				free(aux);

			}else{
				aux = tabla->pedidos;
				tabla->pedidos = tabla->pedidos->siguiente;
				free(aux);
			}

		}

	}
	eliminarPlani(idDisco);
	return 1;
}

int agregarPedido(int dPedido, unsigned char pedido, int sector, char *buffer) // idDisco, dPedido quien lo pidio
{

	struct t_pedido* 		nuevo;

	nuevo = (struct t_pedido*) malloc (sizeof(struct t_pedido));

	nuevo->descriptorSoc = dPedido;
	nuevo->sector = sector;
	nuevo->type = pedido;
	nuevo->buffer = buffer;
	nuevo->siguiente = NULL;

	if(tablaprox == NULL)
	{
		tablaprox = tablaDesc;
	}

	if(pedido == write_c)
	{
		while((tablaprox != NULL) && (tablaprox->idDisco == 1) && ((tablaprox->idDisco == 0)) && (tablaprox->descriptorSoc != dPedido)) //que no sea fs
		{
			tablaprox = tablaprox->siguiente;
		}
		if(tablaprox == NULL)
		{
			return 0;
		}else{
			if(tablaprox == tablaDesc)
			{
				pthread_mutex_lock(&l_ped);
			}

			if(tablaprox->pedidos == NULL)
			{
				tablaprox->pedidos = nuevo;
				ultimo = nuevo;
			}else{
				ultimo->siguiente = nuevo;

			}

			if(tablaprox == tablaDesc)
			{
				pthread_mutex_unlock(&l_ped);
			}
			actualizarBitMap_cero(sector, tablaprox->bitmap);

			sem_post(&tablaprox->contador);

			tablaprox = tablaprox->siguiente;

		return tablaprox->idDisco;
		}
	}else if (pedido == read_c){
		// si es lectura en el bit map tiene que estar en 1 ese sector

		while((tablaprox != NULL) && (tablaprox->descriptorSoc == dPedido) && (tablaprox->idDisco == 1) && ((tablaprox->idDisco == 0)) && (!sePuedeLeer(sector, tablaprox->bitmap)))
		{
			tablaprox = tablaprox->siguiente;
		}
		if(tablaprox == NULL)
		{
			return 0;
		}else{
			//le envio a la consola para que imprima

//			h = descriptorDelPlani(0); //descriptor de la consola
//			nipc_0payload(&msg, h);
//
//			msg.type = read_c; //lectura a un plani
//
//			sprintf(n, "%d;", tablaprox->idDisco);
//			msg.payload = (char*) malloc( strlen(n) + 1 );
//
//			sprintf(msg.payload,"%d;", tablaprox->idDisco);
//
//			msg.payloadLength = strlen(msg.payload) + 1;
//
//			memset(msg.payloadID, 0, 16);
//			sprintf(msg.payloadID,"%d", fdConsola);
//
//			nipc_send(&msg, fdConsola);


			if(tablaprox == tablaDesc)
			{
				pthread_mutex_lock(&l_ped);
			}
			if(tablaprox->pedidos == NULL)
			{
				tablaprox->pedidos = nuevo;
				ultimo = nuevo;

			}else{
				ultimo->siguiente = nuevo;
			}

			sem_post(&tablaprox->contador);

			if(tablaprox == tablaDesc)
			{
				pthread_mutex_unlock(&l_ped);
			}
		}
		return 1;
	}
return 0;
}

int tareaCompleta(int fdCon, int sector, int pedido, char * buffer)
{
	struct descriptor_t*	tabla = NULL;
	struct t_pedido*		ped = NULL;
	struct t_pedido*		aux = NULL;
	nipcMessage				nipc;


	tabla = tablaDesc;
	while((tabla != NULL) && (tabla->descriptorSoc != fdCon))
	{
		tabla = tabla->siguiente;
	}
	if(tabla == NULL)
	{
		return 0;
	}else{
		ped = tabla->pedidos;
		while((ped->siguiente != NULL) && (ped->siguiente->sector != sector) && (ped->siguiente->type != pedido))
		{
			ped = ped->siguiente;
		}
		/*if(ped->siguiente == NULL)
		{
			return 0;
		}else{*/
			//si es lectura tenemos que ver quien lo pidio porq si fue un
			//plani hay que mandar esa escritura porq es redundancia

			if(ped->type == write_c){ // es escritura
				actualizarBitMap_uno(ped->sector, tabla->bitmap);

//				h = descriptorDelPlani(0); //descriptor de la consola
//				nipc_0payload(&nipc, h);
//
//				//le mando a consola
//				nipc.type = escritura_c;
//				sprintf(nipc.payload, "%d;%d", h, tabla->idDisco);
//				nipc.payloadLength = strlen(nipc.payload) + 1;
//				nipc_send(&nipc, h);

				return 1;
			}
			if((idPlaniDelDescriptor(ped->descriptorSoc)) == 1) // lo pidio el fs y es lectura
			{
				aux = ped->siguiente;

				if(ped == tabla->pedidos)
				{
					pthread_mutex_lock(&l_ped);
				}

				sem_wait(&(tabla->contador));
				if(ped == NULL)
				{
					pthread_mutex_unlock(&l_ped);
				}

				nipc_0payload(&nipc, aux->descriptorSoc);
				nipc.type = aux->type;
				sprintf(nipc.payload, "%d", aux->sector);
				strcat(nipc.payload, ";");
				strcat(nipc.payload, buffer);

				nipc_send(&nipc, aux->descriptorSoc);

//				h = descriptorDelPlani(0); //descriptor de la consola
//				nipc_0payload(&nipc, h);
//
//				sprintf(m, "%d;", tablaprox->idDisco);
//				nipc.payload = (char*) malloc( strlen(m) + 1 );
//
//				sprintf(nipc.payload,"%d;", tablaprox->idDisco);
//
//				//le mando a consola
//				nipc.type = escritura_c;
//				sprintf(nipc.payload, "%d;", tabla->idDisco);
//				nipc.payloadLength = strlen(nipc.payload) + 1;
//				nipc_send(&nipc, h);

				free(aux);
				return 1;

			}else{ // lo pidio un plani y es lectura, ahora lo tengo que mandar a escritura

				aux = ped;

				if(ped == tabla->pedidos)
				{
					pthread_mutex_lock(&l_ped);
				}

				ped = ped->siguiente;

				if(ped == NULL)
				{
					pthread_mutex_unlock(&l_ped);
				}

				/*h = descriptorDelPlani(0); //descriptor de la consola
				nipc_0payload(&nipc, h);

				sprintf(m, "%d;", tablaprox->idDisco);
				nipc.payload = (char*) malloc( strlen(m) + 1 );

				sprintf(nipc.payload,"%d;", tablaprox->idDisco);

				//le mando a consola
				nipc.type = lectura_c;
				sprintf(nipc.payload, "%d;", tabla->idDisco);
				nipc.payloadLength = strlen(nipc.payload) + 1;
				nipc_send(&nipc, h);
				*/
				agregarPedido(aux->descriptorSoc , write_c, aux->sector, buffer);
				free(aux);
				return 2;

			}

		}
	return 0;
}


/*int eliminarPedido(int dPedido, char pedido, int sector, char *buffer)
{

}*/
void atenderPlani(int fdCon)
{
	int				dPlani, idDisco;
	char			pedido;
	int				sector;
	char*			buffer;
	nipcMessage 	nipc;
	int				j;


	j = nipc_recv(&nipc, fdCon);
	puts("llego algo del plani");
	if(j == NIPC_CONNECTION_LOST)
	{
		if (cantidadPlani() >= 1)
		{
			idDisco = idPlaniDelDescriptor(fdCon);

			enviarAOtroPlani(idDisco);
		}else{
			exit(1);
		}
	}else{
		pedido = nipc.type;
		dPlani = atoi(strtok(nipc.payload, ";"));
		sector = atoi(strtok(NULL, ";"));
		//me esta respondiendo a una escritura, me importa lo que tiene por si es un error
		//me esta respondiendo a una lectura, o sea que tiene lo leido en el buffer o error

		buffer = strtok(NULL, ";");
		//fdCon a quien le mande el pedido
		tareaCompleta(fdCon, sector, pedido, buffer);
	}
}

void* enviarPlani(void* params)
{
	int						idDisco, fdCon;
	struct descriptor_t*	tabla;
	struct t_pedido*		pedidos = NULL;


	fdCon = *((int*)params);

	free(params);

	tabla = tablaDesc;

	while((tabla!= NULL) && (tabla->descriptorSoc != fdCon))
	{
		tabla = tabla->siguiente;
	}
	if(tabla != NULL)
	{
		pedidos = tabla->pedidos;
		idDisco = tabla->idDisco;
	}
	while(1)
	{
		sem_wait(&tabla->contador);
		enviar(idDisco, fdCon, tabla->pedidos);
		tabla->pedidos = tabla->pedidos->siguiente;

	}

return NULL;
}


int enviar(int idDisco, int fdCon, struct t_pedido* pedidos)
{
	nipcMessage				nipc;
	int						j = 0;
	char				n[14] = "";

	nipc_0payload(&nipc, fdCon);
	sprintf(n, "%d;%d;", idDisco, pedidos->sector);

	nipc.type = pedidos->type;

	if(nipc.type == read_c)
	{
		nipc.payload = (char*) malloc( strlen(n) + 1 );
		memset(nipc.payload, '0', strlen(nipc.payload));
		sprintf(nipc.payload, "%d;%d;", idDisco, pedidos->sector);

	}else if(nipc.type == write_c)
	{
		nipc.payload = (char*) malloc( strlen(pedidos->buffer) + strlen(n) + 1 );
		memset(nipc.payload, '0', strlen(nipc.payload));
		sprintf(nipc.payload, "%d;%d;", idDisco, pedidos->sector);

		strcat(nipc.payload, pedidos->buffer);
		strcat(nipc.payload, ";");
	}

	nipc.payloadLength = strlen(nipc.payload) + 1;
	j = nipc_send(&nipc, fdCon);
	if(j == NIPC_ERROR_NONE)
	{
		return 1;
	}else if ((j == NIPC_ERROR) || (j == NIPC_CONNECTION_LOST))
	{
		if (cantidadPlani() >= 1)
		{
			enviarAOtroPlani(idDisco);

			return 1;
		}else{

			exit(1);
		}
		free(nipc.payload);
	}

return 0;
}

void* redundancia(void* params)
{
	int 				numSec, fdCon;
	int 				idDescPlani = 0;
	int					j=0, idDisco;

	idDisco = *((int*)params);

	free(params);

	//creamos el bit map to do con cero
	fdCon = descriptorDelPlani(idDisco);

	j = cantidadPlani();
	if(j == 1)
	{
		//maestro, todoo bien
		cargoBitMap_uno(cantSecTotal, tablaDesc->bitmap);

		return NULL;
	}else if(j >= 2)
	{
		//copiar del primero, si se cae el maestro se cierra el raid
		//simplemente con mandar a leer o escribir ahi tengo que verificar que este conectado(error 22)
		numSec = 0;

		while(numSec <= cantSecTotal) //no me puedo pasar del sector
		{
			//no leo de este idDisco, todoo lo contrario; por eso cero

			idDescPlani = agregarPedido(idDisco, read_c, numSec, NULL);

			if(idDescPlani == 0)
			{
				printf("no hay planificadores");
			}
//				else{
//
//				sprintf(n, "%d;%d", idDisco, numSec);
//				nipc.payload = (char*) malloc( strlen(n) + 1 );
//
//				sock = descriptorDelPlani(idDescPlani);
//				nipc_0payload(&nipc, sock);
//				nipc.type = read_c;
//				sprintf(nipc.payload, "%d;%d", idDescPlani, numSec);
//
//				h = nipc_send(&nipc,sock);
//				if(h == NIPC_ERROR)
//				{
//					h = nipc_send(&nipc, sock);
//					free(nipc.payload);
//				}
//				if((h == NIPC_CONNECTION_LOST) || (h == NIPC_ERROR))
//				{
//					//todo pthread_mutex_lock(&l_pedidos);
//					enviarAOtroPlani(idDisco);
//					// todo pthread_mutex_unlock(&l_pedidos);
//					pthread_mutex_lock(&descriptor);
//					eliminarPlani(idDisco);
//					pthread_mutex_unlock(&descriptor);
//					free(nipc.payload);
//					return 2;
//				}
				//todo cambiar en el bit map ese sector a 1
			numSec ++;
			sleep(retardo);

//			}
		}

	}
pthread_exit(NULL);
return NULL;
}

int crearBitMap(int sectores, struct bit_map * bitmap)
{
	int i;

	bitmap = (struct bit_map*) malloc (sizeof(struct bit_map));
	for(i= 0; i < (sectores-1); i++){

		(*bitmap).b[i] = 0;
	}

return 1;
}
//
//int crearArchivoYBitMap(int fdCon)
//{
//	FILE	*file;
//	char	cmd[40];
//
//	sprintf(cmd, "mkdir \"%d\"", fdCon); /* todo \fdcon\ */
//
//	system(cmd);
//
//	sprintf(cmd, "%d/bitmap", fdCon);
//
//	if(NULL == (file = fopen(cmd, "w+b")))
//		return 1;
//
//	fclose(file);
//
//	chdir("..");
//
//	return 0;
//}
//

int cargoBitMap_uno(int sectores, struct bit_map* bitmap)
{
	int i;

	for(i= 0; i < sectores; i++){

		(*bitmap).b[i] = 1;
	}
	return 1;
}

int actualizarBitMap_uno(int sector, struct bit_map * bitmap)
{

	(*bitmap).b[(sector-1)] = 1;
	return 1;
}
int actualizarBitMap_cero(int sector, struct bit_map * bitmap)
{
	(*bitmap).b[(sector-1)] = 0;
	return 1;
}

int sePuedeLeer(int sector, struct bit_map* bitmap)
{
	if((*bitmap).b[(sector-1)] == 1)
	{
		return 1;
	}
	return 0;

}




